﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using SmartSpace.Core;
using SmartSpace.Data.Util;

namespace SmartSpace.Data
{
    public class DataService : IDataService
    {
        private IDbScriptService _dbScriptService { get; set; }
        private IDataFactory _contextFactory { get; set; }

        public DataService(IDataFactory contextFactory)
        {
            _contextFactory = contextFactory;
        }

        ///// <summary>
        ///// 获取空的数据对象
        ///// <para>一些系统的值会被自动赋予</para>
        ///// </summary>
        ///// <param name="view"></param>
        ///// <param name="user"></param>
        ///// <returns></returns>
        //public DataInstance GetEmpty(ViewMetadata view, User user)
        //{
        //    DetailData data = new DetailData();
        //    data.Entity = view.Entity;
        //    data.Fields = new List<FieldInstance>();
        //    foreach (SmartSpace.Core.Metadata.Workflow.FieldMetadata fieldMetadata in view.Fields)
        //    {
        //        FieldInstance field = new FieldInstance();
        //        field.Property = fieldMetadata.Property;
        //        field.SortIndex = fieldMetadata.SortIndex;

        //        // 标识自动分配GUID
        //        //if (field.Metadata.Property.IsIdentity)
        //        //{
        //        //    field.SetValue(Guid.NewGuid().ToString());
        //        //}
        //        //if (field.Metadata.Property.Type.Name == "create.user")
        //        //{
        //        //    field.SetValue(user.Id.ToString("N"));
        //        //}
        //        field.Default(user);

        //        data.Fields.Add(field);
        //    }
        //    return data;
        //}

        //public bool Store(User user, DetailData data)
        //{
        //    if (data.GetIdentity().Value == null)
        //    {
        //        data.GetIdentity().SetValue(Guid.NewGuid().ToString());
        //    }
        //    return _dbScriptService.Store(data);
        //}

        //public DetailData Store(User user, DetailData data, AuthorizedView view)
        //{
        //    Guid id = data.GetIdentity().GetValue<Guid>();
        //    if (data.GetIdentity().Value == null)
        //    {
        //        id = Guid.NewGuid();
        //        data.GetIdentity().SetValue(id.ToString());
        //    }

        //    if (_dbScriptService.Store(data))
        //    {
        //        return GetDetailData(view, id);
        //    }
        //    else
        //    {
        //        return null;
        //    }
        //}

        //public ListData GetListData(ActivityMetadata activity, int start, int limit)
        //{
        //    return _dbScriptService.GetListData(activity, start, limit);
        //}

        //public DetailData GetDetailData(ViewMetadata view, Guid id)
        //{
        //    using (SsDbContext context = _contextFactory.GetValue())
        //    {
        //        DetailData data = null;
        //        try
        //        {
        //            data = new DetailData();
        //            data.Fields = new List<FieldInstance>();
        //            data.Entity = view.Entity;

        //            context.Database.Connection.Open();

        //            System.Data.Common.DbCommand command = context.Database.Connection.CreateCommand();

        //            List<DbParameter> parameters;
        //            string scriptSql = DbScriptUtil.GetSelectScript(view.Entity, id, command, out parameters);

        //            command.CommandText = scriptSql;
        //            command.CommandType = System.Data.CommandType.Text;
        //            command.Parameters.AddRange(parameters.ToArray());
        //            using (System.Data.Common.DbDataReader reader = command.ExecuteReader())
        //            {
        //                if (reader.Read())
        //                {
        //                    foreach (SmartSpace.Core.Metadata.Workflow.FieldMetadata fieldMetadata in view.Fields)
        //                    {
        //                        FieldInstance fieldData = new FieldInstance();
        //                        fieldData.Property = fieldMetadata.Property;
        //                        fieldData.SortIndex = fieldMetadata.SortIndex;
        //                        fieldData.SetValue(Convert.ToString(reader[fieldMetadata.Property.Name]));
        //                        data.Fields.Add(fieldData);
        //                    }
        //                }
        //            }
        //        }
        //        catch
        //        {
        //            data = null;
        //        }
        //        finally
        //        {
        //            context.Database.Connection.Close();
        //        }
        //        return data;
        //    }
        //}

        //public DetailData GetDetailData(AuthorizedView view, Guid id)
        //{
        //    using (SsDbContext context = _contextFactory.GetValue())
        //    {
        //        DetailData data = null;
        //        try
        //        {
        //            data = new DetailData();
        //            data.Fields = new List<FieldInstance>();
        //            data.Entity = view.Entity;

        //            context.Database.Connection.Open();

        //            System.Data.Common.DbCommand command = context.Database.Connection.CreateCommand();

        //            List<DbParameter> parameters;
        //            string scriptSql = DbScriptUtil.GetSelectScript(view.Entity, id, command, out parameters);

        //            command.CommandText = scriptSql;
        //            command.CommandType = System.Data.CommandType.Text;
        //            command.Parameters.AddRange(parameters.ToArray());
        //            using (System.Data.Common.DbDataReader reader = command.ExecuteReader())
        //            {
        //                if (reader.Read())
        //                {
        //                    foreach (SmartSpace.Core.Metadata.Workflow.AuthorizedField field in view.Fields)
        //                    {
        //                        FieldInstance fieldData = new FieldInstance();
        //                        //fieldData.Metadata = field.
        //                        fieldData.Property = field.Property;
        //                        fieldData.SortIndex = field.SortIndex;
        //                        fieldData.SetValue(Convert.ToString(reader[field.Property.Name]));
        //                        data.Fields.Add(fieldData);
        //                    }
        //                }
        //            }
        //        }
        //        catch
        //        {
        //            data = null;
        //        }
        //        finally
        //        {
        //            context.Database.Connection.Close();
        //        }
        //        return data;
        //    }
        //}

        public bool ToTable(EntityMetadata metadata)
        {
            using (DataContext context = _contextFactory.Get())
            {
                string scriptSql = DbScriptUtil.GetScript(metadata);
                try
                {
                    context.Database.ExecuteSqlCommand(scriptSql);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }

        public PagedList<EntityMetadata> FindEntities(int start, int limit)
        {
            using (DataContext context = _contextFactory.Get())
            {
                List<EntityMetadata> list = context.Set<EntityMetadata>()
                    .OrderByDescending(p => p.Name)
                    .Skip(start)
                    .Take(limit)
                    .ToList();
                PagedList<EntityMetadata> pagedList = new PagedList<EntityMetadata>();
                foreach (EntityMetadata item in list)
                {
                    item.Properties = context.Set<PropertyMetadata>()
                        .Include("Type")
                        .Where(p => p.EntityId == item.Id)
                        .OrderBy(p => p.OrderIndex)
                        .ToList();
                    pagedList.Add(item);
                }
                pagedList.Start = start;
                pagedList.Limit = limit;
                pagedList.Total = context.Set<EntityMetadata>().Count();

                return pagedList;
            }
        }

        public EntityMetadata FindEntity(Guid id)
        {
            using (DataContext context = _contextFactory.Get())
            {
                EntityMetadata entity = context.Set<EntityMetadata>()
                    .Where(p => p.Id == id)
                    .FirstOrDefault();
                entity.Properties = context.Set<PropertyMetadata>()
                    .Include("Type")
                    .Where(p => p.EntityId == entity.Id)
                    .OrderBy(p => p.OrderIndex)
                    .ToList();

                return entity;
            }
        }

        public bool StoreEntity(EntityMetadata metadata)
        {
            using (DataContext context = _contextFactory.Get())
            {
                foreach (PropertyMetadata property in metadata.Properties)
                {
                    property.Type = null;
                }

                if (context.Set<EntityMetadata>()
                    .Where(p => p.Id == metadata.Id)
                    .Select(p => new { p.Id })
                    .FirstOrDefault() == null)
                {
                    context.Set<EntityMetadata>().Add(metadata);
                }
                else
                {
                    List<PropertyMetadata> properties = context.Set<PropertyMetadata>()
                        .Where(p => p.EntityId == metadata.Id)
                        .ToList();
                    foreach (PropertyMetadata pro in properties)
                    {
                        bool isExist = false;
                        PropertyMetadata updatePro = null;
                        foreach (PropertyMetadata proNew in metadata.Properties)
                        {
                            if (proNew.Id == pro.Id)
                            {
                                updatePro = proNew;
                                isExist = true;
                                break;
                            }
                        }

                        if (!isExist)
                        {
                            // 删除
                            context.Set<PropertyMetadata>().Remove(pro);
                        }
                        else
                        {
                            // 更新
                            context.Entry(pro).State = System.Data.Entity.EntityState.Detached;
                            context.Entry(updatePro).State = System.Data.Entity.EntityState.Modified;
                        }
                    }

                    foreach (PropertyMetadata proNew in metadata.Properties)
                    {
                        bool isExist = false;
                        foreach (PropertyMetadata pro in properties)
                        {
                            if (proNew.Id == pro.Id)
                            {
                                isExist = true;
                                break;
                            }
                        }

                        if (!isExist)
                        {
                            // 添加
                            context.Set<PropertyMetadata>().Add(proNew);
                        }
                    }

                    context.Entry(metadata).State = System.Data.Entity.EntityState.Modified;

                }

                int count = context.SaveChanges();
                return true;
            }
        }

        public bool StoreType(TypeMetadata metadata)
        {
            using (DataContext context = _contextFactory.Get())
            {
                if (context.Set<TypeMetadata>()
                    .Where(p => p.Id == metadata.Id)
                    .Select(p => new { p.Id })
                    .FirstOrDefault() == null)
                {
                    context.Set<TypeMetadata>().Add(metadata);
                }
                else
                {
                    context.Set<TypeMetadata>().Attach(metadata);
                }

                context.SaveChanges();
                return true;
            }
        }

        public PagedList<TypeMetadata> FindTypes(int start, int limit)
        {
            using (DataContext context = _contextFactory.Get())
            {
                List<TypeMetadata> list = context.Set<TypeMetadata>()
                    .OrderByDescending(p => p.Caption)
                    .Skip(start)
                    .Take(limit)
                    .ToList();
                PagedList<TypeMetadata> pagedList = new PagedList<TypeMetadata>();
                foreach (TypeMetadata item in list)
                {
                    pagedList.Add(item);
                }
                pagedList.Start = start;
                pagedList.Limit = limit;
                pagedList.Total = context.Set<TypeMetadata>().Count();

                return pagedList;
            }
        }

        public TypeMetadata FindType(Guid id)
        {
            using (DataContext context = _contextFactory.Get())
            {
                return context.Set<TypeMetadata>()
                    .Where(p => p.Id == id)
                    .SingleOrDefault();
            }
        }

        public bool SaveRelation(EntityMetadata primaryEntity, EntityMetadata relationEntity, bool isInline)
        {
            using (DataContext context = _contextFactory.Get())
            {
                if (context.Set<EntityRelation>()
                    .Where(p => p.PrimaryEntityId == primaryEntity.Id && p.RelationEntityId == relationEntity.Id).Select(p => new { p.Id }).SingleOrDefault() == null)
                {
                    EntityRelation relation = new EntityRelation();
                    relation.Id = Guid.NewGuid();
                    relation.IsInline = isInline;
                    relation.PrimaryEntityId = primaryEntity.Id;
                    relation.RelationEntityId = relationEntity.Id;
                    context.Set<EntityRelation>().Add(relation);
                    if (context.SaveChanges() > 0)
                    {
                        return true;
                    }

                }
                return false;
            }
        }

        public List<EntityMetadata> FindRelationEntities(Guid entityId)
        {
            using (DataContext context = _contextFactory.Get())
            {
                return context.Set<EntityMetadata>().Select(p => p)
                    .Join(
                        context.Set<EntityRelation>().Where(r => r.PrimaryEntityId == entityId),
                        p => p.Id,
                        v => v.RelationEntityId,
                        (p, v) => p).ToList();
            }
        }

        public EntityMetadata FindPrimaryEntity(Guid entityId)
        {
            using (DataContext context = _contextFactory.Get())
            {
                return context.Set<EntityMetadata>().Select(p => p)
                    .Join(
                        context.Set<EntityRelation>().Where(r => r.RelationEntityId == entityId),
                        p => p.Id,
                        v => v.PrimaryEntityId,
                        (p, v) => p).SingleOrDefault();
            }
        }

        public bool SaveDictionaryType(DictionaryType type)
        {
            using (DataContext context = _contextFactory.Get())
            {
                if (context.Set<DictionaryType>()
                    .Where(p => p.Id == type.Id)
                    .Select(p => new { p.Id })
                    .FirstOrDefault() == null)
                {
                    context.Set<DictionaryType>().Add(type);
                }
                else
                {
                    context.Set<DictionaryType>().Attach(type);
                }

                context.SaveChanges();
                return true;
            }
        }

        public List<DictionaryType> FindDictionaryTypes()
        {
            using (DataContext context = _contextFactory.Get())
            {
                return context.Set<DictionaryType>()
                    .OrderBy(p => p.OrderIndex).ToList();
            }
        }

        public bool SaveDictionaryData(DictionaryData data)
        {
            using (DataContext context = _contextFactory.Get())
            {
                if (context.Set<DictionaryData>()
                    .Where(p => p.Id == data.Id)
                    .Select(p => new { p.Id })
                    .FirstOrDefault() == null)
                {
                    data.Type = null;
                    data.Parent = null;
                    context.Set<DictionaryData>().Add(data);
                }
                else
                {
                    context.Set<DictionaryData>().Attach(data);
                }

                context.SaveChanges();
                return true;
            }
        }

        public List<DictionaryData> FindDictionaryDatas(Guid typeId)
        {
            List<DictionaryData> roots = new List<DictionaryData>();
            List<DictionaryData> datas = null;

            using (DataContext context = _contextFactory.Get())
            {
                datas = context.Set<DictionaryData>()
                    .Where(p => p.TypeId == typeId)
                    .OrderBy(p => p.OrderIndex).ToList();
            }

            for (int index = 0; index < datas.Count; )
            {
                DictionaryData item = datas[index];
                if (item.ParentId == null || item.ParentId == default(Guid))
                {
                    roots.Add(item);
                    datas.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }

            return roots;
        }

        public List<DictionaryData> FindDictionaryDatas(DictionaryType type)
        {
            List<DictionaryData> roots = new List<DictionaryData>();
            Stack<DictionaryData> stacks = new Stack<DictionaryData>();
            List<DictionaryData> datas = null;

            using (DataContext context = _contextFactory.Get())
            {
                datas = context.Set<DictionaryData>()
                    .Where(p => p.TypeId == type.Id)
                    .OrderBy(p => p.OrderIndex).ToList();
            }

            for (int index = 0; index < datas.Count; )
            {
                DictionaryData item = datas[index];
                if (item.ParentId == null || item.ParentId == default(Guid))
                {
                    roots.Add(item);
                    stacks.Push(item);
                    datas.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }

            //while (stacks.Count > 0)
            //{
            //    DictionaryData parent = stacks.Pop();

            //    for (int index = 0; index < datas.Count; )
            //    {
            //        DictionaryData item = datas[index];
            //        if (item.ParentId == parent.Id)
            //        {
            //            parent.Children.Add(item);
            //            stacks.Push(item);
            //            datas.RemoveAt(index);
            //        }
            //        else
            //        {
            //            index++;
            //        }
            //    }
            //}

            return roots;
        }

        public ViewMetadata GetView(Guid entityId)
        {
            using (DataContext context = _contextFactory.Get())
            {
                EntityMetadata entity = this.FindEntity(entityId);

                ViewMetadata view = new ViewMetadata();

                return view;
            }
        }

        public DataInstance Store(ViewMetadata view, DataInstance data, User user)
        {
            using (DataContext context = _contextFactory.Get())
            {
                //fix:system type property
                foreach (var fieldItem in data.Fields)
                {
                    // 系统字段，并且为空就自动赋值
                    // 标识字段不会自动赋值
                    if (fieldItem.Property.Type.IsSystem 
                        && !fieldItem.Property.IsIdentity
                        && fieldItem.Data == null)
                    {
                        AutoInputs.Get(fieldItem.Property.Type.Name).Fix(fieldItem, user);
                    }
                }

                DbParameter[] parameters = null;
                DbCommand command = context.Database.Connection.CreateCommand();
                string sqlScript = string.Empty;
                if (data.Id == Guid.Empty)
                {
                    data.Id = Guid.NewGuid();
                    // 添加
                    sqlScript = DbScriptUtil.GetInsertScript(
                        view,
                        data,
                        command,
                        out parameters);
                    if (!string.IsNullOrEmpty(sqlScript))
                    {
                        context.Database.Connection.Open();
                        command.CommandText = sqlScript;
                        command.CommandType = System.Data.CommandType.Text;
                        command.Parameters.AddRange(parameters);
                        try
                        {
                            if (command.ExecuteNonQuery() > 0)
                            {
                                return data;
                            }
                        }
                        catch
                        {
                        }
                        finally
                        {
                            context.Database.Connection.Close();
                        }
                    }
                    data.Id = Guid.Empty;
                }
                else
                {
                    // 更新
                    sqlScript = DbScriptUtil.GetUpdateScript(
                        view,
                        data,
                        command,
                        out parameters);
                    if (!string.IsNullOrEmpty(sqlScript))
                    {
                        context.Database.Connection.Open();
                        command.CommandText = sqlScript;
                        command.CommandType = System.Data.CommandType.Text;
                        command.Parameters.AddRange(parameters);
                        try
                        {
                            if (command.ExecuteNonQuery() > 0)
                            {
                                return data;
                            }
                        }
                        catch
                        {
                        }
                        finally
                        {
                            context.Database.Connection.Close();
                        }
                    }
                }
            }
            return null;
        }

        public DataInstance GetData(ViewMetadata view, User user, Guid dataId)
        {
            if (dataId == Guid.Empty)
            {
                DataInstance instance = new DataInstance();
                instance.Entity = view.Entity;

                instance.Fields = new List<FieldInstance>();
                foreach (var field in view.Fields)
                {
                    FieldInstance fInst = new FieldInstance();
                    fInst.Property = field.Property;
                    fInst.OrderIndex = field.OrderIndex;

                    instance.Fields.Add(fInst);
                }

                return instance;
            }
            else
            {
                using (DataContext context = _contextFactory.Get())
                {
                    DataInstance data = null;
                    try
                    {
                        data = new DataInstance();
                        data.Fields = new List<FieldInstance>();
                        data.Entity = view.Entity;

                        context.Database.Connection.Open();

                        System.Data.Common.DbCommand command = context.Database.Connection.CreateCommand();

                        List<DbParameter> parameters;
                        string scriptSql = DbScriptUtil.GetSelectScript(view, dataId, command, out parameters);

                        command.CommandText = scriptSql;
                        command.CommandType = System.Data.CommandType.Text;
                        command.Parameters.AddRange(parameters.ToArray());
                        using (System.Data.Common.DbDataReader reader = command.ExecuteReader())
                        {
                            if (reader.Read())
                            {
                                foreach (FieldMetadata fieldMetadata in view.Fields)
                                {
                                    FieldInstance fieldData = new FieldInstance();
                                    fieldData.Property = fieldMetadata.Property;
                                    fieldData.OrderIndex = fieldMetadata.OrderIndex;
                                    object dbValue = reader[fieldMetadata.Property.Name];
                                    if (dbValue != DBNull.Value)
                                    {
                                        if (fieldMetadata.Property.Type.IsComplex)
                                        {
                                            object dbValueText = reader[fieldMetadata.Property.Name + "_text"];
                                            FieldInstance.SetData(fieldData, Convert.ToString(dbValue), Convert.ToString(dbValueText));
                                        }
                                        else
                                        {
                                            FieldInstance.SetData(fieldData, Convert.ToString(dbValue));
                                        }
                                    }
                                    data.Fields.Add(fieldData);
                                }
                            }
                        }
                    }
                    catch
                    {
                        data = null;
                    }
                    finally
                    {
                        context.Database.Connection.Close();
                    }
                    return data;
                }
            }
        }

        public PagedList<DataInstance> GetListData(ViewMetadata view, int start, int limit)
        {
            using (DataContext context = _contextFactory.Get())
            {
                PagedList<DataInstance> list = new PagedList<DataInstance>();
                list.Start = start;
                list.Limit = limit;

                try
                {
                    context.Database.Connection.Open();

                    System.Data.Common.DbCommand command = context.Database.Connection.CreateCommand();

                    List<DbParameter> parameters;
                    string scriptSql = DbScriptUtil.GetPagedListScript(view, start, limit, command, out parameters);

                    command.CommandText = scriptSql;
                    command.CommandType = System.Data.CommandType.Text;
                    command.Parameters.AddRange(parameters.ToArray());

                    using (System.Data.Common.DbDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            DataInstance data = null;
                            data = new DataInstance();
                            data.Fields = new List<FieldInstance>();
                            data.Entity = view.Entity;
                            foreach (FieldMetadata fieldMetadata in view.Fields)
                            {
                                FieldInstance fieldData = new FieldInstance();
                                fieldData.Property = fieldMetadata.Property;
                                fieldData.OrderIndex = fieldMetadata.OrderIndex;
                                object dbValue = reader[fieldMetadata.Property.Name];
                                if (dbValue != DBNull.Value)
                                {
                                    FieldInstance.SetData(fieldData, Convert.ToString(dbValue));
                                }
                                data.Fields.Add(fieldData);
                            }

                            list.Add(data);
                        }
                    }

                    // 获取总数
                    List<DbParameter> totalParameters = null;
                    System.Data.Common.DbCommand totalCommand = context.Database.Connection.CreateCommand();
                    string totalSql = DbScriptUtil.GetTotalScript(view, totalCommand, out totalParameters);
                    totalCommand.CommandText = totalSql;
                    totalCommand.CommandType = System.Data.CommandType.Text;
                    totalCommand.Parameters.AddRange(totalParameters.ToArray());
                    using (System.Data.Common.DbDataReader totalReader = totalCommand.ExecuteReader())
                    {
                        if (totalReader.Read())
                        {
                            list.Total = Convert.ToInt32(totalReader[0]);
                        }
                    }
                }
                catch
                {
                }
                finally
                {
                    context.Database.Connection.Close();
                }
                return list;
            }
        }
    }
}
